package cn.lg.soar.core.handler.log;

import cn.lg.soar.common.model.LogDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 日志处理支持
 * @author luguoxiang
 * @date 2022/4/24
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public abstract class LogCollectorSupport implements ILogCollector{

    protected static final Logger LOGGER = LoggerFactory.getLogger(LogCollectorSupport.class);

    /**
     * 批量存储条数
     */
    private final static int BATCH_SIZE = 100;

    /**
     * 使用线程池异步处理日志保存
     */
    private final ThreadPoolExecutor pool = new ThreadPoolExecutor(
            1,
            10,
            180L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadPoolExecutor.DiscardPolicy()
    );

    /**
     * 日志暂存
     */
    private volatile List<LogDTO> list = new ArrayList<>(512);

    private final IOperationLogSaveService operationLogSaveService;

    public LogCollectorSupport(IOperationLogSaveService operationLogSaveService) {
        this.operationLogSaveService = operationLogSaveService;
    }

    @Override
    public synchronized void save(LogDTO log) {
        if (list.size() > BATCH_SIZE) {
            this.pool.execute(this::saveLog);
        }
        list.add(log);
    }

    @Override
    public void saveSync(LogDTO log) {
        ArrayList<LogDTO> logs = new ArrayList<>();
        logs.add(log);
        try {
            operationLogSaveService.saveLog(logs);
        } catch (RuntimeException e) {
            LogCollectorSupport.LOGGER.error("同步保存日志失败", e);
        }
    }

    /**
     * 触发一次日志保存操作，将收集的日志保存到数据库
     */
    protected void saveLog() {
        List<LogDTO> list;
        synchronized (this) {
            list = this.list;
            this.list = new ArrayList<>(512);
        }
        if (list.isEmpty()) {
            return;
        }
        int x = 0;
        int len;
        do {
            len = list.size() - x;
            batchSaveLog(
                    list.subList(x, Math.min(len, BATCH_SIZE))
            );
            x += BATCH_SIZE;
        } while (x < list.size());
    }

    /**
     * 批量保存日志
     * @param operationLogs
     */
    protected void batchSaveLog(List<LogDTO> operationLogs) {
        operationLogSaveService.saveLog(operationLogs);
    }

}
